package com.xhwl.centiotdata.suppliersystem.eesd.renke;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
import com.xhwl.centiotdata.pojo.constant.RenkeConstant;
import com.xhwl.centiotdata.pojo.dto.eesd.EesdConfigDTO;
import com.xhwl.centiotdata.pojo.enums.eesd.EesdMethodEnum;
import com.xhwl.centiotdata.pollingtrigger.eesd.EesdDispatcher;
import com.xhwl.centiotdata.service.cent.IBiEnvCentService;
import com.xhwl.centiotdata.service.cent.IDeviceCentService;
import com.xhwl.centiotdata.service.integrateconfig.EESDIntegrationConfig;
import com.xhwl.centiotdata.utils.RedisUtils;
import com.xhwl.common.dto.cent.device.DeviceDTO;
import com.xhwl.common.enums.device.DeviceOnlineStatusEnum;
import com.xhwl.common.enums.iot.EnvEnum;
import com.xhwl.common.pojo.ResultJson;
import com.xhwl.common.pojo.cent.bi.DwdFactEnvironmentMetrics;
import com.xhwl.common.pojo.cent.device.Device;
import com.xhwl.common.pojo.cent.device.DeviceProjSupplierCategory;
import com.xhwl.common.utils.ChkUtil;
import com.xhwl.common.utils.DateUtils;
import com.xhwl.common.vo.cent.eesd.EnvRenKeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.groovy.util.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author： lianghz
 * @Description：
 * @Date 2021/09/03/ 09:44
 */
@RestController
@Slf4j
public class RenkeApiHandler {

    @Autowired
    private EesdDispatcher eesdDispatcher;

    @Autowired
    private IBiEnvCentService iBiEnvCentService;

    @Autowired
    private IDeviceCentService iDeviceCentService;

    private static ThreadLocal<List<Map<String, Object>>> threadLocal = new ThreadLocal<>();

    @Autowired
    RedisUtils redisUtils;

    @Autowired
    private EESDIntegrationConfig integrationConfig;


    /**
     * 对外接口，老接口，保留
     */
    @RequestMapping(value = "/renke/pull")
    public ResultJson pullInfo(@RequestParam(value = "projectId") Integer projectId) {
        Map<String, Object> thirdData = getThirdData(projectId);
        if (thirdData == null) {
            return ResultJson.success(Collections.emptyList());
        }
        return ResultJson.success(Collections.singletonList(thirdData));
    }

    /**
     * 实时数据
     *
     * @return
     */
    @RequestMapping(value = "/renke/live")
    public ResultJson<EnvRenKeVO> getLiveData(@RequestParam(value = "projectId") Integer projectId) {
        Map<String, Object> thirdData = getThirdData(projectId);
        if (thirdData == null)
            return ResultJson.fail("获取数据失败");
        EnvRenKeVO envRenKeVO = transformToVO(thirdData);
        return ResultJson.success(envRenKeVO);
    }

    /**
     * 执行一些测试或数据补救操作
     *
     * @param projectId
     * @param type
     * @return
     */
    @RequestMapping(value = "/renke/execute")
    public ResultJson executeRenKe(@RequestParam(value = "projectId") Integer projectId, @RequestParam(value = "type") Integer type) {
        Map<String, Object> thirdData = getThirdData(projectId);
        switch (type) {
            case 0:
                //入库最新数据
//                boolean b = storageDB(thirdData, projectId);
//                if (b) {
//                    return ResultJson.success("最新数据入库成功");
//                } else {
//                    return ResultJson.fail("最新数据入库失败");
//                }
            default:
                return ResultJson.fail("不支持的type类型");
        }
    }

    /**
     * 拉取记录并入库
     */
    public void pullRecordAndStorage(EesdConfigDTO config) {
        pullRecord(config);
        List<Map<String, Object>> eesdRenKeVO = threadLocal.get();
        threadLocal.remove();
        List<Map<String, Object>> mapList = parseMapToList(eesdRenKeVO);
        storageDB(mapList, config.getProjectId());
    }

    public List<DwdFactEnvironmentMetrics> pullEnvDevice(DeviceProjSupplierCategory config) {
        EesdConfigDTO eesdConfigDTO = getConfig(config.getProjectId());
        return pullRecord(eesdConfigDTO);
    }

    private EesdConfigDTO getConfig(Integer projectId) {
        if (projectId == null)
            return null;
        List<EesdConfigDTO> eesdConfigs = integrationConfig.getIntegrationConfig(EesdMethodEnum.RENKE.getId());
        for (EesdConfigDTO eesdConfig : eesdConfigs) {
            if (eesdConfig.getProjectId().equals(projectId)) {
                return eesdConfig;
            }
        }
        return null;
    }

    /**
     * 获取第三方数据
     */
    public List<DwdFactEnvironmentMetrics> pullRecord(EesdConfigDTO eesdConfigDTO) {
        try {
            String userId = (String) redisUtils.get("RENKE_USER_ID");
            String host;
            String res;
            Map<String, Object> map;
            if (ChkUtil.isNull(userId)) {
                //获取登录用户名密码
                host = eesdConfigDTO.getHost() + RenkeConstant.LOGIN_URL;
                Unirest.setTimeouts(0, 0);
                Map<String, String> of = Maps.of("loginName", eesdConfigDTO.getLoginName(), "password", eesdConfigDTO.getPassword());
                HttpResponse<String> response = Unirest.post(host)
                        .header("Content-Type", "application/json")
                        .body(JSON.toJSONString(of))
                        .asString();
                res = new String(response.getBody().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
                map = JSON.parseObject(res);
                if (map.get("data") instanceof String) {
                    log.error("{}", map.get("data"));
                    threadLocal.set(Lists.newArrayList());
                    return null;
                }
                Map<String, Object> data = (Map<String, Object>) map.get("data");
                if (ChkUtil.isNull(data)) {
                    threadLocal.set(Lists.newArrayList());
                    return null;
                }
                if (ChkUtil.isNull(data.get("userId"))) {
                    log.error("{},{},无法获取用户Id", eesdConfigDTO, res);
                    threadLocal.set(Lists.newArrayList());
                    return null;
                }
                userId = data.get("userId") + "";
                redisUtils.setWithMinute("RENKE_USER_ID", userId, 60);
            }
            //获取风能实时数据
            Unirest.setTimeouts(0, 0);
            host = eesdConfigDTO.getHost() + RenkeConstant.GET_DEVICE_DATA;
            HttpResponse<String> dataRes = Unirest.get(host)
                    .header("userId", userId)
                    .asString();
            res = new String(dataRes.getBody().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
            map = JSON.parseObject(res);
            List<Map<String, Object>> dataList = (List<Map<String, Object>>) map.get("data");
            if (CollectionUtil.isEmpty(dataList)) {
                log.error("{},用户ID:{},原因:{},无法获取实时数据", eesdConfigDTO, userId, res);
                threadLocal.set(Lists.newArrayList());
                return null;
            }
            threadLocal.set(dataList);
            synToDevice(eesdConfigDTO, dataList);
            return getEnvObj(dataList, eesdConfigDTO.getProjectId());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void synToDevice(EesdConfigDTO eesdConfigDTO, List<Map<String, Object>> dataList) {
//        List<DwdFactEnvironmentMetrics> dwdFactEnvironmentMetrics = getEnvObj(dataList, eesdConfigDTO.getProjectId());
//        if (CollectionUtils.isEmpty(dwdFactEnvironmentMetrics)) {
//            return;
//        }
//        List<Map<String, Object>> realData = parseMapToList(dataList);
        List<DeviceDTO> deviceDTOList = new ArrayList<>();
        List<Device> deviceList = new ArrayList<>();
        for (Map<String, Object> data : dataList) {
            String deviceId = ChkUtil.getString(data.get("deviceKey"));
            String deviceName = ChkUtil.getString(data.get("deviceName"));
            String isOnline = ChkUtil.getString(data.get("deviceStatus"));
            DeviceDTO query = new DeviceDTO();
            query.setProjectId(eesdConfigDTO.getProjectId());
            query.setThirdDeviceId(deviceId);
            query.setName(deviceName);
            query.setDeviceTypeId(8);
            query.setCategoryId(8);
            query.setDeviceStatus(1);
            query.setProductId(EesdMethodEnum.RENKE.getId());
            int onlineStatus = "2".equalsIgnoreCase(isOnline) ? 1 : 0;
            query.setOnlineStatus(onlineStatus);
            query.setAttributeJson(JSON.parseObject(JSON.toJSONString(data)));

            Device device = new Device();
            device.setProjectId(eesdConfigDTO.getProjectId());
            device.setThirdDeviceId(deviceId);
            device.setDeviceTypeId(8);
            device.setOnlineStatus(onlineStatus == 1 ? DeviceOnlineStatusEnum.ONLINE : DeviceOnlineStatusEnum.OFFLINE);

            deviceDTOList.add(query);
            deviceList.add(device);
        }

        deviceDTOList = deviceDTOList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<DeviceDTO>(Comparator.comparing(DeviceDTO::getThirdDeviceId))), ArrayList::new));
        deviceList = deviceList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<Device>(Comparator.comparing(Device::getThirdDeviceId))), ArrayList::new));
        ResultJson resultJson = iDeviceCentService.asyncUpdateDevice(deviceDTOList);
        //todo 测试环境cent-device更新后，这个方法可以删除
        ResultJson resultJson1 = iDeviceCentService.asyncUpdateOnlineStatus(deviceList);
        log.info("同步蓝居设备数据:" + deviceDTOList.toString());

    }

    private List<DwdFactEnvironmentMetrics> getEnvObj(List<Map<String, Object>> dataList, Integer projectId) {
        List<Map<String, Object>> realData = parseMapToList(dataList);
        if (CollectionUtils.isEmpty(realData)) {
            return Collections.emptyList();
        }
        return realData.stream().map(e -> transformToDB(e, projectId)).collect(Collectors.toList());
    }

    private Map<String, Object> getThirdData(Integer projectId) {
        eesdDispatcher.executeRenKeInfo(projectId);
        List<Map<String, Object>> eesdRenKeVO = threadLocal.get();
        threadLocal.remove();
        //处理数据
        if (eesdRenKeVO == null) {
            return null;
        }
        return parseMap(eesdRenKeVO);
    }

    private Map<String, Object> parseMap(List<Map<String, Object>> list) {
        if (list == null || list.size() == 0) {
            return null;
        }
        Map<String, Object> real = new HashMap<>(16);
        for (Map<String, Object> map : list) {

            List<Map<String, Object>> realTimeData = (List<Map<String, Object>>) map.get("realTimeData");
            if (realTimeData == null) {
                continue;
            }
            for (Map<String, Object> realTimeDatum : realTimeData) {
                real.put((String) realTimeDatum.get("dataName"), realTimeDatum.get("dataValue"));
            }
        }
        return real;
    }

    private List<Map<String, Object>> parseMapToList(List<Map<String, Object>> list) {
        if (list == null || list.size() == 0) {
            return null;
        }
        List<Map<String, Object>> resultList = new ArrayList<>();
        Map<String, Object> real = new HashMap<>(16);
        for (Map<String, Object> map : list) {

            List<Map<String, Object>> realTimeData = (List<Map<String, Object>>) map.get("realTimeData");
            if (realTimeData == null) {
                continue;
            }
            for (Map<String, Object> realTimeDatum : realTimeData) {
                real.put((String) realTimeDatum.get("dataName"), realTimeDatum.get("dataValue"));
            }
            resultList.add(real);
        }
        return resultList;
    }

    /**
     * 环境数据入库
     *
     * @param mapList
     * @param projectId
     * @return
     */
    private boolean storageDB(List<Map<String, Object>> mapList, Integer projectId) {
        if (CollectionUtils.isEmpty(mapList))
            return false;
        List<DwdFactEnvironmentMetrics> transform = mapList.stream().map(e -> transformToDB(e, projectId)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(transform)) {
            iBiEnvCentService.asyncEnvInitData(JSON.toJSONString(transform));
            return true;
        }
        return false;
    }

    /**
     * 属性转换
     * map的值结构为
     * {
     * "日雨量(mm)": "0.00",
     * "累积雨量(mm)": "1666.40",
     * "噪声(dB)": "43.80",
     * "湿度(%RH)": "42.80",
     * "pm2.5(ug/m³)": "35.00",
     * "瞬时雨量(mm)": "0.00",
     * "风向": "东南风",
     * "风力(级)": "1.00",
     * "pm10(ug/m³)": "54.00",
     * "温度(℃)": "23.60",
     * "风速(m/s)": "0.70",
     * "大气压(Kpa)": "101.70",
     * "当前雨量(mm)": "0.00"
     * }
     *
     * @param map
     * @return
     */
    private DwdFactEnvironmentMetrics transformToDB(Map<String, Object> map, Integer projectId) {
        try {
            DwdFactEnvironmentMetrics dwdFactEnvironmentMetrics = new DwdFactEnvironmentMetrics();
            LocalDateTime currentLocalDateTime = DateUtils.getCurrentLocalDateTime();
            dwdFactEnvironmentMetrics.setObsTime(currentLocalDateTime);
            dwdFactEnvironmentMetrics.setProjectId(projectId);
            addProperty(map, dwdFactEnvironmentMetrics, DwdFactEnvironmentMetrics.class);

            dwdFactEnvironmentMetrics.setDeviceCode((String) map.get("deviceKey"));
            return dwdFactEnvironmentMetrics;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private EnvRenKeVO transformToVO(Map<String, Object> map) {
        EnvRenKeVO envRenKeVO = new EnvRenKeVO();
        addProperty(map, envRenKeVO, EnvRenKeVO.class);
        return envRenKeVO;
    }

    private void addProperty(Map<String, Object> map, Object target, Class clazz) {
        for (String s : map.keySet()) {
            EnvEnum key = EnvEnum.getKey(s);
            if (key != null) {
                if (key.getClazz() == Double.class) {
                    Double d = Double.parseDouble(map.get(s).toString());
                    setField(getField(key.name(), clazz), target, d);
                } else if (key.getClazz() == String.class) {
                    String str = (String) map.get(s);
                    setField(getField(key.name(), clazz), target, str);
                }
            }
        }
    }

    private void setField(Field field, Object target, Object value) {
        if (field == null || target == null)
            return;
        if (!Modifier.isPublic(field.getModifiers())) {
            ReflectionUtils.makeAccessible(field);
        }
        ReflectionUtils.setField(field, target, value);
    }

    private Field getField(String name, Class clazz) {
        return ReflectionUtils.findField(clazz, name);
    }

}
